package net.yurushao.encrypt;

import java.io.InputStream;
import java.io.OutputStream;

import javax.crypto.Cipher;
import javax.crypto.CipherOutputStream;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.PBEParameterSpec;

/**
 * 
 * @author shaoyuru@whu.edu.cn
 * 
 */
public class Encrypter {
	Cipher ecipher;

	/**
	 * Construct a encrypter object
	 * 
	 * @param key
	 *            Use this key to generate secret key
	 * @param saltStr
	 *            Special parameter to generate secret key
	 */
	public Encrypter(String key, String saltStr) {
		byte[] salt = makeSalt(saltStr);
		PBEParameterSpec paramSpec = new PBEParameterSpec(salt, 20);
		PBEKeySpec keySpec = new PBEKeySpec(key.toCharArray());

		try {
			SecretKeyFactory skf = SecretKeyFactory
					.getInstance("PBEWithMD5AndDES");
			SecretKey skey = skf.generateSecret(keySpec);
			ecipher = Cipher.getInstance("PBEWithMD5AndDES");
			ecipher.init(Cipher.ENCRYPT_MODE, skey, paramSpec);
		} catch (Exception e) {
			e.printStackTrace();
		}

	}

	/**
	 * Make a 8 bytes salt from a string
	 * 
	 * @param saltStr
	 *            The string to make salt
	 * @return A 8 bytes salt
	 */
	private byte[] makeSalt(String saltStr) {
		byte[] saltBytes = saltStr.getBytes();
		int len = saltBytes.length;
		byte[] s = new byte[8];

		if (len < 8) {
			for (int i = 0; i < len; i++)
				s[i] = saltBytes[i];
			for (int i = len; i < 8; i++)
				s[i] = (byte) (i * 10);
		} else { // len >=8, pick out first 8 bytes
			for (int i = 0; i < 8; i++)
				s[i] = saltBytes[i];
		}

		return s;
	}

	public Cipher getCipher() {
		return ecipher;
	}

	/**
	 * Encrypt the input stream and output
	 * 
	 * @param in
	 *            InputStream that will be encrypted
	 * @param out
	 *            OutputStream that have been encrypted
	 */
	public void encrypt(InputStream in, OutputStream out) {
		byte[] buf = new byte[1024];
		try {
			out = new CipherOutputStream(out, ecipher);
			int read = 0;
			while ((read = in.read(buf)) > 0) {
				out.write(buf, 0, read);
			}
			in.close();
			out.close();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	public byte[] encrypt(byte[] input) {
		byte[] encrypted = null;
		try {
			encrypted = ecipher.doFinal(input);
		} catch (Exception e) {
			e.printStackTrace();
		}
		return encrypted;
	}

}
